﻿#region File and License Information
/*
<File>
	<License Type="BSD">
		Copyright © 2009 - 2012, Outcoder. All rights reserved.
	
		This file is part of Calcium (http://calciumsdk.net).

		Redistribution and use in source and binary forms, with or without
		modification, are permitted provided that the following conditions are met:
			* Redistributions of source code must retain the above copyright
			  notice, this list of conditions and the following disclaimer.
			* Redistributions in binary form must reproduce the above copyright
			  notice, this list of conditions and the following disclaimer in the
			  documentation and/or other materials provided with the distribution.
			* Neither the name of the <organization> nor the
			  names of its contributors may be used to endorse or promote products
			  derived from this software without specific prior written permission.

		THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
		ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
		WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
		DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
		DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
		(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
		LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
		ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
		(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
		SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	</License>
	<Owner Name="Daniel Vaughan" Email="danielvaughan@outcoder.com" />
	<CreationDate>2010-11-20 13:29:36Z</CreationDate>
</File>
*/
#endregion

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

using Outcoder.UI.Xaml.Controls.Design;

using Microsoft.Phone.Shell;

namespace Outcoder.UI.Xaml.Controls
{
    public class AppBarToggleButton : FrameworkElement,
        IApplicationBarIconButtonProvider
    {
        public ApplicationBarIconButton Button { get; set; }

        /* This handler is a field because there is an issue 
         * with the handler being cleaned up, and GC.KeepAlive doesn't work. */
        readonly EventHandler command1CanExecuteChangedHandler;
        readonly EventHandler command2CanExecuteChangedHandler;

        public AppBarToggleButton()
        {
            Button = new ApplicationBarIconButton
                     	{
                     		Text = "Text",
							/* IconUri must be set. */
							IconUri = new Uri("/Images/Unknown.png", UriKind.Relative) 
						};
            
            command1CanExecuteChangedHandler = Command1_CanExecuteChanged;
            command2CanExecuteChangedHandler = Command2_CanExecuteChanged;
        	
			//Loaded += delegate { UpdateToggleState(); };
	        Loaded += HandleLoaded;
	        Unloaded += HandleUnloaded;
        }

	    void HandleLoaded(object sender, RoutedEventArgs e)
	    {
			Button.Click -= ApplicationBarIconButtonClick;
			Button.Click += ApplicationBarIconButtonClick;
	    }

	    void HandleUnloaded(object sender, RoutedEventArgs e)
	    {
			Button.Click -= ApplicationBarIconButtonClick;
	    }

	    static void UpdateToggleState(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateToggleState();
        }

        void UpdateToggleState()
        {
            bool toggled = Toggled;

            UpdateText(toggled);
            UpdateIconUri(toggled);
            UpdateCommand(toggled);
        }

        static void UpdateText(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateText(button.Toggled);
        }

        void UpdateText(bool toggled)
        {
            Button.Text = toggled ? Text2 ?? Text1 : Text1;
        }

        static void UpdateIconUri(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateIconUri(button.Toggled);
        }

        void UpdateIconUri(bool toggled)
        {
            Button.IconUri = toggled
                ? IconUri2 ?? IconUri1 : IconUri1;
        }

        static void UpdateCommand(DependencyObject d)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }
            button.UpdateCommand(button.Toggled);
        }

        void UpdateCommand(bool toggled)
        {
			ICommand command2 = Command2;

			if (HasIsEnabledBinding())
			{
				return;
			}

			if (toggled && command2 != null)
            {
				IsEnabled = command2.CanExecute(CommandParameter2);
            }
            else
            {
                ICommand command = Command1;
				if (command != null)
				{
					IsEnabled = command.CanExecute(CommandParameter1);
				}
            }
        }

        #region IconUri1 Dependency Property

        public static DependencyProperty Icon1UriProperty = DependencyProperty.Register(
            "IconUri1",
            typeof(Uri),
            typeof(AppBarToggleButton),
            new PropertyMetadata(HandleIcon1Changed));

        static void HandleIcon1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateToggleState(d);
        }

        public Uri IconUri1
        {
            get
            {
                return (Uri)GetValue(Icon1UriProperty);
            }
            set
            {
                SetValue(Icon1UriProperty, value);
            }
        }

        #endregion

        #region IconUri2 Dependency Property

        public static DependencyProperty Icon2UriProperty
            = DependencyProperty.Register(
                "IconUri2",
                typeof(Uri),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleIcon2Changed));

        static void HandleIcon2Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateIconUri(d);
        }

        [Description("The location of the toggled state icon.")]
        public Uri IconUri2
        {
            get
            {
                return (Uri)GetValue(Icon2UriProperty);
            }
            set
            {
                SetValue(Icon2UriProperty, value);
            }
        }

        #endregion

        #region Command 1

        public static readonly DependencyProperty Command1Property
            = DependencyProperty.RegisterAttached(
                "Command1",
                typeof(ICommand),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommand1Changed));

        public ICommand Command1
        {
            get
            {
                return (ICommand)GetValue(Command1Property);
            }
            set
            {
                SetValue(Command1Property, value);
            }
        }

        static void HandleCommand1Changed(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }

            if (e.OldValue != null)
            {
                ((ICommand)e.OldValue).CanExecuteChanged
                    -= button.command1CanExecuteChangedHandler;
            }

            if (e.NewValue != null)
            {
	            var newCommand = (ICommand)e.NewValue;
				newCommand.CanExecuteChanged 
					-= button.command1CanExecuteChangedHandler;
				newCommand.CanExecuteChanged
					+= button.command1CanExecuteChangedHandler;
            }
        }

        void Command1_CanExecuteChanged(object sender, EventArgs e)
        {
			if (Toggled && Command2 != null || HasIsEnabledBinding())
            {
                return;
            }

            ICommand command = Command1;
            if (command != null)
            {
                IsEnabled = command.CanExecute(CommandParameter1);
            }
        }

        public static readonly DependencyProperty CommandParameter1Property
            = DependencyProperty.RegisterAttached(
                "CommandParameter1",
                typeof(object),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommandParameter1Changed));

        static void HandleCommandParameter1Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateCommand(d);
        }

        public object CommandParameter1
        {
            get
            {
                return GetValue(CommandParameter1Property);
            }
            set
            {
                SetValue(CommandParameter1Property, value);
            }
        }
        #endregion

        #region Command 2

        public static readonly DependencyProperty Command2Property
            = DependencyProperty.RegisterAttached(
                "Command2",
                typeof(ICommand),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommand2Changed));

        public ICommand Command2
        {
            get
            {
                return (ICommand)GetValue(Command2Property);
            }
            set
            {
                SetValue(Command2Property, value);
            }
        }

        static void HandleCommand2Changed(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var button = d as AppBarToggleButton;
            if (button == null)
            {
                return;
            }

            if (e.OldValue != null)
            {
                ((ICommand)e.OldValue).CanExecuteChanged
                    -= button.command2CanExecuteChangedHandler;
            }

            if (e.NewValue != null)
            {
                ((ICommand)e.NewValue).CanExecuteChanged
                    += button.command2CanExecuteChangedHandler;
            }
        }

        void Command2_CanExecuteChanged(object sender, EventArgs e)
        {
			if (!Toggled || HasIsEnabledBinding())
            {
                return;
            }

            ICommand command = Command2;
            if (command != null)
            {
                IsEnabled = command.CanExecute(CommandParameter2);
            }
        }

        public static readonly DependencyProperty CommandParameter2Property
            = DependencyProperty.RegisterAttached(
                "CommandParameter2",
                typeof(object),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleCommandParameter2Changed));

        static void HandleCommandParameter2Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateCommand(d);
        }

        public object CommandParameter2
        {
            get
            {
                return GetValue(CommandParameter2Property);
            }
            set
            {
                SetValue(CommandParameter2Property, value);
            }
        }
        #endregion

        #region IsEnabled

        public static readonly DependencyProperty IsEnabledProperty
            = DependencyProperty.RegisterAttached(
                "IsEnabled",
                typeof(bool),
                typeof(AppBarToggleButton),
                new PropertyMetadata(true, HandleIsEnabledChanged));

        public bool IsEnabled
        {
            get
            {
                return (bool)GetValue(IsEnabledProperty);
            }
            set
            {
                SetValue(IsEnabledProperty, value);
            }
        }

        static void HandleIsEnabledChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AppBarToggleButton iconButton;
            if (e.NewValue != e.OldValue
                && (iconButton = d as AppBarToggleButton) != null
                && iconButton.Button != null)
            {
                iconButton.Button.IsEnabled = (bool)e.NewValue;
            }
        }

		bool HasIsEnabledBinding()
		{
			BindingExpression expression = GetBindingExpression(IsEnabledProperty);
			return expression != null;
		}

        #endregion

        #region Text 1

        public static readonly DependencyProperty Text1Property
            = DependencyProperty.RegisterAttached(
                "Text1",
                typeof(string),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleText1Changed));

        public string Text1
        {
            get
            {
                return (string)GetValue(Text1Property);
            }
            set
            {
                SetValue(Text1Property, value);
            }
        }

        static void HandleText1Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateText(d);
        }

        #endregion

        #region Text 2

        public static readonly DependencyProperty Text2Property
            = DependencyProperty.RegisterAttached(
                "Text2",
                typeof(string),
                typeof(AppBarToggleButton),
                new PropertyMetadata(HandleText2Changed));

        public string Text2
        {
            get
            {
                return (string)GetValue(Text2Property);
            }
            set
            {
                SetValue(Text2Property, value);
            }
        }

        static void HandleText2Changed(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateText(d);
        }

        #endregion

        #region Toggled

        public static DependencyProperty ToggledProperty
            = DependencyProperty.Register(
                "Toggled",
                typeof(bool),
                typeof(AppBarToggleButton),
                new PropertyMetadata(false, HandleToggledChanged));

        static void HandleToggledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UpdateToggleState(d);
        }

        public bool Toggled
        {
            get
            {
                return (bool)GetValue(ToggledProperty);
            }
            set
            {
                SetValue(ToggledProperty, value);
            }
        }

        #endregion

        #region Click Event

        public event EventHandler Click;

        void ApplicationBarIconButtonClick(object sender, EventArgs e)
        {
            ICommand command;
            object commandParameter;
            if (!Toggled)
            {
                command = Command1;
                commandParameter = CommandParameter1;
            }
            else
            {
                command = Command2 ?? Command1;
                commandParameter = CommandParameter2 ?? CommandParameter1;
            }

            if (command != null)
            {
                command.Execute(commandParameter);
            }
            else
            {
				/* No command to execute therefore rely entirely on the toggle state. */
				Toggled = !Toggled;
            }

            OnClick(e);
        }

        protected virtual void OnClick(EventArgs e)
        {
            var tempEvent = Click;
            if (tempEvent != null)
            {
                tempEvent(this, e);
            }
        }

        #endregion
    }
}